﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace SharpShare.Collections {
    [Serializable]
    public sealed class Path<TComponent> : IEquatable<Path<TComponent>> where TComponent : class {
        private List<TComponent> _components = new List<TComponent>();

        public static readonly Path<TComponent> Empty = new Path<TComponent>();

        public Path(IEnumerable<TComponent> components) {
            if (components != null) {
                _components.AddRange(components);
            }
        }
        public Path(params TComponent[] components)
            : this((IEnumerable<TComponent>)components) {

        }

        public ReadOnlyCollection<TComponent> Components {
            get {
                return _components.AsReadOnly();
            }
        }

        public int Count {
            get {
                return this.Components.Count;
            }
        }

        public TComponent this[int index] {
            get {
                return this.Components[index];
            }
        }

        public TComponent First {
            get {
                return this[0];
            }
        }
        public TComponent Last {
            get {
                return this[this.Count - 1];
            }
        }

        public Path<TComponent> Add(IEnumerable<TComponent> components) {
            return new Path<TComponent>(this.Components.Concat(components));
        }
        public Path<TComponent> Add(params TComponent[] components) {
            return this.Add((IEnumerable<TComponent>)components);
        }

        public Path<TComponent> Up {
            get {
                return new Path<TComponent>(this.Components.Take(this.Count - 1));
            }
        }

        public bool IsDescendant(Path<TComponent> otherPath) {
            if (otherPath.Count < this.Count) {
                return false;
            }

            for (int x = 0; x < this.Count; x++) {
                if (!this[x].Equals(otherPath[x])) {
                    return false;
                }
            }

            return true;
        }


        public override string ToString() {
            return this.ToString("/");
        }

        public string ToString(string pathSeparator) {
            return string.Join(pathSeparator, this.Components.Select(c => c.ToString()));
        }

        public override bool Equals(object obj) {
            return Equals(obj as Path<TComponent>);
        }

        public override int GetHashCode() {
            return this.Components.GetHashCode();
        }

        public bool Equals(Path<TComponent> other) {
            if (other == null) {
                return false;
            }

            if (this.Count != other.Count) {
                return false;
            }

            for (int x = 0; x < this.Count; x++) {
                if (!this[x].Equals(other[x])) {
                    return false;
                }
            }

            return true;
        }

        public static bool operator ==(Path<TComponent> path1, Path<TComponent> path2) {
            if (object.ReferenceEquals(path1, null) || object.ReferenceEquals(path2, null)) {
                return false;
            }

            return path1.Equals(path2);
        }
        public static bool operator !=(Path<TComponent> path1, Path<TComponent> path2) {
            return !(path1 == path2);
        }
        public static Path<TComponent> operator +(Path<TComponent> path1, Path<TComponent> path2) {
            return path1.Add(path2.Components);
        }
        public static Path<TComponent> operator +(Path<TComponent> path1, TComponent path2) {
            return path1.Add(path2);
        }
        public static Path<TComponent> operator +(TComponent path1, Path<TComponent> path2) {
            return new Path<TComponent>(path1).Add(path2.Components);
        }
    }

    public static class Path {
        public static Path<string> Parse(string path, char componentSeparator = '/', string currentToken = ".", string parentToken = "..") {
            path = path.TrimStart(componentSeparator);

            if (string.IsNullOrEmpty(path)) {
                return Path<string>.Empty;
            }

            string[] split = path.Split(componentSeparator);
            Path<string> finalPath = Path<string>.Empty;

            foreach (string s in split) {
                if (currentToken != null && currentToken == s) {

                } else if (parentToken != null && parentToken == s) {
                    finalPath = finalPath.Up;
                } else {
                    finalPath += new Path<string>(s);
                }
            }

            return finalPath;
        }
    }

}
